Глава 5

ВВЕДЕНИЕ В OBJECT PASCAL

В этой и последующих главах второй части книги мы будем изучать основной инструмент Delphi - язык Object Pascal. Назначение этой главы - предоставить вам возможность неформального знакомства с основными свойствами языка без точного описания и углубленного изучения его конструкций.

5.1. УЧЕБНАЯ ПРОГРАММА

При описании свойств языка мне понадобится иллюстрировать их небольшими примерами. В таких примерах чаще всего реализуются простые действия по вводу некоторых данных, их преобразованию и выводу результатов на экран. Для реализации этих действий создадим форму, которая будет содержать все необходимые для учебных примеров интерфейсные элементы. Чтобы эта форма была доступна в любой учебной программе, мы поместим ее в так называемый репозиторий Delphi - архив, в котором сохраняются заготовки для новых программ. Замечу, что в версии 1 нет репозитория. Тем не менее полезно и в этом случае создать учебную форму: чтобы избавить себя от повторения одних и тех же действий при реализации разных примеров. Сохраните ее в файле с именем Example, например, а при создании новой программы свяжите ее с проектом (опция File | Add) и сразу же сохраните под другим именем (File | Save As).

Вид формы для учебной программы показан на рис. 5.1. На форме размещена панель (трапе!), две кнопки (TBitBtn), строка ввода (TEdit), метка (TLabel) и многострочный редактор (тмето). Панель понадобилась для того, чтобы размещенные на ней кнопки, строка ввода и метка всегда располагались в нижней части окна, даже если окно распахнуто на весь экран. Левая кнопка ок служит для сообщения программе о том, что в строке ввода подготовлен текст. Обработчик события onciick этой кнопки будет тем самым местом программы, куда мы будем помещать учебные фрагменты. Правая кнопка close служит для завершения работы программы. Ее функции аналогичны функциям системной кнопки закрытия окна, и поэтому ее можно было бы и не размещать на форме. Однако я предпочитаю использовать крупную, бросающуюся в глаза кнопку, чтобы даже неопытный пользователь сразу увидел интерфейсный элемент, с помощью которого он сможет прекратить работу с программой. Метка располагается над строкой ввода и пока не видна на экране. Она предназначена для размещения коротких сообщений, которые будет формировать программа. Для вывода многострочных сообщений служит редактор TMеmо, занимающий все остальное место окна.

Рис. 5.1. Форма для учебной программы

5.1.1. Конструирование формы

Вы можете самостоятельно сконструировать форму для учебной программы, руководствуясь приведенным выше описанием размещенных на ней компонентов и рисунком 5.1. Для облегчения этого процесса ниже для каждого компонента указаны свойства, значения которых отличаются от умалчиваемых. Компоненты перечислены в порядке их размещения, т. е. вначале подготавливаются свойства формы, потом на форму помещается панель, на нее помещаются кнопки и строка ввода, а метка и многострочный редактор - на свободное место формы над панелью.

Размеры компонентов установлены с учетом разрешения экрана 800х600. Для другого разрешения эти размеры, возможно, потребуется скорректировать.

Форма

Свойство

Значение

Caption

Height

Name

Position

Width

Учебная программа

375

fmExample

poScreenCenter

544

 

Панель (компонент panel на странице Standard). Кладется на любое свободное место формы.

Свойство

Значение

Align

BevelOuter Caption

Height

AlBottom

BvNone Удалить

84

 

Кнопка OK (компонент BitBtn на странице Additional). Кладется на любое свободное место панели.

Свойство

Значение

Kind

bkOk

Left

32

Name

bbRun

Тор

48

 

Кнопка close (компонент BitBtn на странице Additional). Кладется на любое свободное место панели.

Свойство

Значение

Kind

bkClose

Left

128

Name

BbClose

Тор

48

 

Строка ввода (компонент Edit на странице standard). Кладется на любое свободное место панели.

Свойство

Значение

Name

edinput

Left

128

Text

Удалить

Top

16

Width

289

Метка (компонент Label на странице Standard). Кладется на любое свободное место формы.

Свойство

Значение

Align

Caption

Name

alBottom

Удалить

IbOutput

 

Многострочный редактор (компонент Memo на странице Standard). Кладется на любое свободное место формы.

Свойство

Значение

Align

alClient

Lines

Удалить

Name

mmOutput

ScrollBars

ssBoth

Wordwrap

False

Следует прокомментировать сделанные нами изменения умалчиваемых значений свойств.

Размеры формы Height (Высота) и Width (Ширина) совпадают с умалчиваемыми. Они выбраны так, чтобы форма не казалась слишком маленькой или слишком большой на экранах с разным разрешением - они оптимальны для типичного на сегодня разрешения 800х600. Свойство Position управляет размещением формы относительно границ экрана. Умалчиваемое значение poDesigned потребует разместить форму так, как она была размещена на этапе конструирования, рекомендуемое значение poScreencenter - симметрично по центру экрана.

Свойство Align указывает способ выравнивания размещаемого компонента относительно того контейнера, в котором он размещается. Контейнером называется компонент, предназначенный для размещения в нем других компонентов. Пустая форма представляет собой контейнер, в который помещаются другие компоненты. Таким же контейнером является компонент panel. Мы разместили на форме панель и в ее свойство Align установили значение alBottom (Внизу). После установки этого значения компонент (в данном случае - панель) прижимается к нижней границе формы и растягивается по всей ее длине. Теперь панель и все размещенные на ней компоненты будут примыкать к нижнему краю окна, даже если окно распахивается на весь экран. Если бы мы разместили строку ввода и кнопки не на панели, а непосредственно на форме, они не имели бы свойства перемещения и оставались бы на своих местах при изменении размеров окна [На самом деле это не совсем так. Компонент действительно не будет перемещаться при стандартных (умалчиваемых) значениях его свойства Anchors. Подробнее об этом см. п. 16.4.]. В результате значительная часть полностью распахнутого окна осталась бы пустой и не использовалась.

Свойство caption (Заголовок) присуще всем видимым элементам. Для панели оно определяет текст, который размещается в ее центре. Поскольку мы не намерены размещать на панели никаких текстовых сообщений, это свойство у нее следует очистить.

Чтобы панель и расположенная над ней метка воспринимались как единое целое, в свойстве BevelOuter (Внешняя кромка) панели установлено значение bvNone.

Свойства Left (Слева), тор (Сверху) и width (Ширина) определяют положение левого верхнего угла компонента относительно такого же угла своего контейнера и его (компонента) ширину. Указанные в списке значения этих свойств, для строки ввода обеспечат симметричное по горизонтали расположение строки относительно сторон не распахнутого окна (если, разумеется, вы установили рекомендуемую ширину формы 544). К сожалению, симметрия будет нарушена, если окно распахнется. В Delphi есть средства изменения значений указанных свойств непосредственно в работающей программе. Создав обработчик события ОnResize (По изменению размера) для формы, мы при желании могли бы научить программу реагировать на изменение размеров окна и соответствующим образом изменять значение свойства Left для строки ввода.

Свойство Name (Имя) определяет имя, под которым компонент будет известен программе. Я настоятельно рекомендую вам давать компонентам, на которые ссылается код программы, имена “со смыслом” вместо однотипных имен, которые “придумывает” Delphi. Более того, по принятому в современной практике программирования стилю именам обычно предшествует двухбуквенный префикс. Заложенная в префиксе мнемоника служит напоминанием программисту о типе поименованного компонента или его свойства. Например, все возможные значения свойства Align имеют стандартный для Delphi префикс а1 (от Align), за которым следует вполне понятное английское слово: alBottom, alTop, alLeft и т. д. Строка ввода будет непременным участником кода большинства учебных программ, вот почему я предлагаю назвать этот компонент именем edInput: префикс ed - это сокращение английского названия типа компонента Edit, слово Input означает ввод данных в программу. Такими же осмысленными являются предлагаемые имена для метки - от Label, output - вывод программы), многострочного редактора (mm - от Memo), кнопки OK (bb - ОТ BitBtn, Run - пуск программы) и кнопки close. Панель вряд ли будет упоминаться в программном коде, который вы будете писать, поэтому для нее можно оставить созданное Delphi умалчиваемое имя panel1.

Свойство Text (Текст) у компонента Edit определяет текст, который будет содержать строка ввода в момент появления на экране. Следует очистить это свойство, чтобы начальный текст не мешал вводу данных для программы. Точно так же следует очистить свойство Caption у метки и Lines у многострочного редактора. Чтобы очистить свойство mmOutput. Lines, щелкните по этому свойству в окне Инспектора объектов и вызовите диалоговое окно изменения свойства. Затем удалите умалчиваемый текст Memo1 (или mmoutput, если к этому моменту вы уже дали компоненту рекомендуемое имя) и закройте окно кнопкой OK.

Свойство ScrollBars у компонента mmOutput имеет значение ssBoth (Оба). Такое значение заставит Delphi поместить в редактор обе полосы прокрутки - вертикальную и горизонтальную. Наличие полос поможет вам увидеть текст, отсекаемый границами компонента. В то же время для свойства wordwrap (Перенос слов) советую установить значение False, чтобы отказаться от переноса слов на другую строку редактора в длинных текстовых сообщениях. Для этого компонента свойство Align имеет значение alciient, что потребует от компонента занять всю оставшуюся незаполненной часть формы независимо от размеров окна.

Для компонентов BitBtn свойство Kind (Сорт) определяет типовые функции, которые реализует кнопка в диалоговых окнах. Значение bkCiose определит типовую пиктограмму и надпись close, ко-торые Delphi разместит на кнопке, а также типовую функцию закрытия окна, которая будет связана с этой кнопкой. В результате без малейших усилий с нашей стороны мы получили кнопку, которая завершает работу программы. Значение bkOK заставит кнопку BitBtn закрывать диалоговое окно с результатом тгок. Поскольку наша форма не является диалоговым окном, кнопка ок не будет выполнять эту свою функцию: мы использовали значение bkOK только для того, чтобы кнопка получила нужную пиктограмму и надпись.

5.1.2. Перенос формы в архив

После указанных выше изменений сохраните форму в файле с именем Example в каталоге архива Delphi. Этот каталог имеет имя Objrepos и размещается в каталоге размещения Delphi (по умолчанию - c:\Program Files\Borland\Delphi6). Архив (репозиторий)

Delphi служит для накопления типовых форм и проектов. Использование типовых заготовок стандартизует вид окон и способствует формированию собственного для программиста стиля оформления своих программных проектов.

Перенос формы в каталог Objrepos еще не обеспечивает включение этой формы в архив. Чтобы вызвать диалоговое окно регистрации формы в архиве, щелкните по форме правой кнопкой мыши и в появившемся вспомогательном меню выберите продолжение Add то Repository (см. рис. 5.2).

Рис. 5.2. Диалоговое окно регистрации формы в архиве

Не советую пользоваться опцией меню Project | Add то Repositiry, т. к. в этом случае в репозиторий будет добавлен проект, а не форма. Проекты из репозитория копируются целиком вместе с многочисленными родительскими классами, что замедляет время создания нового проекта и приводит к неоправданным затратам дисковой памяти.

В строке Title напишите имя формы fmExample, В Строке Description — пояснение, например, прототип главной формы для учебной программы, раскройте список page и выберите в нем Forms. Если хотите, укажите в строке Author свою фамилию и закройте окно регистрации кнопкой ок. Теперь форма зарегистрирована, и вы в любой момент сможете выбрать ее с помощью опции File | New | other главного меню Delphi. Однако я предлагаю не успокаиваться на этом и определить нашу форму как главную, тогда Delphi при выборе опции File | New | Application будет именно ее показывать вместо пустой типовой формы. Для этого с помощью опции Tools | Repository главного меню вызовите диалоговое окно, показанное на рис. 5.3.

Рис. 5.3. Окно редактора архива

В списке pages этого окна щелкните по строке Forms и разыщите в самом конце списка объектов objects нашу форму fmExampie. Щелкните по ней, установите флажок в переключателе Main Form (Главная форма) и закройте окно клавишей ок. Когда нам в дальнейшем понадобится создавать проекты, основанные на стандартных пустых формах, вам потребуется вновь вызвать опцию Tools | Repository и убрать флажок в переключателе Main Form.

Чтобы убедиться в правильности ваших действий, закройте проект, ответив нажатием на кнопку No в ответ на предложение Delphi сохранить файл проекта, и выберите File | New | Application-на экране должна появится уже знакомая нам форма учебной программы.

5.2. СТРУКТУРА ПРОГРАММ DELPHI

Любая программа в Delphi состоит из файла проекта (файл с расширением dpr) и одного или нескольких модулей (файлы с расширениями pas). Каждый из таких файлов описывает программную единицу Object Pascal.

5.2.1. Структура проекта

Файл проекта представляет собой программу, написанную на языке Object Pascal и предназначенную для обработки компилятором. Эта программа автоматически создается Delphi и содержит лишь несколько строк. Чтобы увидеть их, запустите Delphi и щелкните по опции Project | View Source главного меню [В предыдущих версиях Delphi для просмотра кода проекта используйте опцию View | project Source.]. Delphi покажет окно кода с закладкой Project1, содержащее такой текст:

program Projecti;

uses

Forms, Unit1 in 'Unit1.pas' {fmExample};

{$R *.RES}

begin

Application.Initialize;

Application.CreateForm(TfmExample, fmExample);

Application.Run;

end.

В окне кода жирным шрифтом выделяются так называемые зарезервированные слова, а курсивом - комментарии (так же выделяются зарезервированные слова и комментарии в книге). Как видим, текст программы начинается зарезервированным словом program и заканчивается словом end с точкой за ним. Замечу, что сочетание end со следующей за ней точкой называется терминатором программной единицы: как только в тексте программы встретится такой терминатор, компилятор прекращает анализ программы и игнорирует оставшуюся часть текста.

Зарезервированные слова играют важную роль в Object Pascal, придавая программе в целом свойство текста, написанного на почти естественном английском языке. Каждое зарезервированное слово (а их в Object Pascal несколько десятков) несет в себе условное сообщение для компилятора, который анализирует текст программы так же, как читаем его и мы: слева направо и сверху вниз.

Комментарии, наоборот, ничего не значат для компилятора, и он их игнорирует. Комментарии важны для программиста, который с их помощью поясняет те или иные места программы. Наличие комментариев в тексте программы делает ее понятнее и позволяет легко вспомнить особенности реализации программы, которую вы написали несколько лет назад. В Object Pascal комментарием считается любая последовательность символов, заключенная в фигурные скобки. В приведенном выше тексте таких комментариев два, но строка

{$R *.RES}

на самом деле не является комментарием. Этот специальным образом написанный фрагмент кода называется директивой компилятора (в нашем случае - указание компилятору на необходимость подключения к программе так называемого файла ресурсов). Директивы начинаются символом $, который стоит сразу за открывающей фигурной скобкой.

Поскольку речь зашла о комментариях, замечу, что в Object Pascal в качестве ограничителей комментария могут также использоваться пары символов (*, *) и //. Скобки (*...*) используются подобно фигурным скобкам т. е. комментарием считается находящийся в них фрагмент текста, а символы // указывают компилятору, что комментарий располагается за ними и продолжается до конца текущей строки:

{Это комментарий}

(*Это тоже комментарий*)

//Все символы до конца этой строки составляют комментарий

Слово Program со следующим за ним именем программы и точкой с запятой образуют заголовок программы. За заголовком следует раздел описаний, в котором программист (или Delphi) описывает используемые в программе идентификаторы. Идентификаторы обозначают элементы программы, такие как типы, переменные, процедуры, функции (об элементах программы мы поговорим чуть позже). Здесь же с помощью предложения, которое начинается зарезервированным словом uses (использовать) программист сообщает компилятору о тех фрагментах программы (модулях), которые необходимо рассматривать как неотъемлемые составные части программы и которые располагаются в других файлах. Строки

uses

Forms, Unit1 in 'Unitl.pas' {fmExample};

указывают, что помимо файла проекта в программе должны использоваться модули Forms И Unit1. модуль Forms является стандартным (т. е. уже известным Delphi), а модуль Unit1 - новым, ранее неизвестным, и Delphi в этом случае указывает также имя файла с текстом модуля (in 'uniti.pas') и имя связанного с модулем файла описания формы {fmExample}.

Собственно тело программы начинается со слова begin (начать) и ограничивается терминатором end с точкой. Тело состоит из нескольких операторов языка Object Pascal. В каждом операторе реализуется некоторое действие - изменение значения переменной, анализ результата вычисления, обращение к подпрограмме и т. п. В теле нашей программы - три исполняемых оператора:

Application.Initialize;

Application.CreateForm(TfmExample, fmExample);

Application.Run;

Каждый из них реализует обращение к одному из методов объекта Application. Я вынужден забежать вперед и хотя бы кратко пояснить только что введенные термины, которые определяют важные для Object Pascal понятия. Объектом называется специальным образом оформленный фрагмент программы, заключающий в себе данные и подпрограммы для их обработки. Данные называются полями объекта, а подпрограммы - его методами. Объект в целом предназначен для решения какой-либо конкретной задачи и воспринимается в программе как неделимое целое (иными словами, нельзя из объекта “выдернуть” отдельное поле или метод). Объекты играют чрезвычайно важную роль в современных языках программирования. Они придуманы для того, чтобы увеличить производительность труда программиста и одновременно повысить качество разрабатываемых им программ. Два главных свойства объекта - функциональность и неделимость - делают его самостоятельной или даже самодостаточной частью программы и позволяют легко переносить объект из одной программы в другую. Разработчики Delphi придумали для нас с вами сотни объектов, которые можно рассматривать как кирпичики, из которых программист строит многоэтажное здание программы. Такой принцип построения программ называется объектно-ориентированным программированием (ООП). В объекте Application собраны данные и подпрограммы, необходимые для нормального функционирования Windows-программы в целом. Delphi автоматически создает объект-программу Application для каждого нового проекта. Строка

Application.Initialize;

означает обращение к методу Initialize объекта Application. Прочитав эту строку, компилятор создаст код, который заставит процессор перейти к выполнению некоторого фрагмента программы, написанного для нас разработчиками Delphi. После выполнения этого фрагмента (программисты говорят: после выхода из подпрограммы) управление процессором перейдет к следующей строке программы, в которой вызывается метод CreateForm и т. д.

5.2.2. Структура модуля

Модули - это программные единицы, предназначенные для размещений фрагментов программ. С помощью содержащегося в них программного кода реализуется вся поведенческая сторона программы. Любой модуль имеет следующую структуру [Более точное описание структуры модуля приводится в п. 12.1.]: заголовок секция интерфейсных объявлений секция реализации терминатор Заголовок открывается зарезервированным словом Unit за которым следует имя модуля и точка с запятой. Секция интерфейсных объявлений открывается зарезервированным словом Interface, a секция реализации - словом implementation. Терминатором модуля, как и терминатором программы, является end с точкой. Следующий фрагмент программы является синтаксически правильным вариантом модуля:

unit Unit1;

interface

// Секция интерфейсных объявлений

implementation

// Секция реализации

end.

В секции интерфейсных объявлений описываются программные элементы (типы, классы, процедуры и функции), которые будут “видны” другим программным модулям, а в секции реализации раскрывается механизм работы этих элементов. Разделение модуля на две секции обеспечивает удобный механизм обмена алгоритмами между отдельными частями одной программы. Он также реализует средство обмена программными разработками между отдельными программистами. Получив откомпилированный “посторонний” модуль, программист получает доступ только к его интерфейсной части, в которой, как уже говорилось, содержатся объявления элементов. Детали реализации объявленных процедур, функций, классов скрыты в секции реализации и недоступны другим модулям.

Щелкните по закладке Unit1 окна кода, и вы увидите такой текст:

 unit Unit1;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls,

Forms, Dialogs, StdCtrls, Buttons, ExtCtrls;

type

TfmExample = class(TForm)

Panel1: TPanel;

bbRun: TBitBtn;

bbClose: TBitBtn;

edinput: TEdit;

IbOutput: TLabel;

mmOutput: TMemo;

private

{ Private declarations } public

{ Public declarations } end;

var

fmExample: TfmExample;

implementation

$R *.DFM}

end.

Весь этот текст сформирован Delphi, но в отличие от файла проекта программист может его изменять, придавая программе нужную функциональность. В интерфейсной секции описан один тип (класс - fmExample) и один объект (переменная fmExample).

Вот описание класса:

type

TfmExample = class(TForm)

Panell: TPanel;

bbRun: TBitBtn;

bbClose: TBitBtn;

edinput: TEdit;

IbOutput: TLabel;

mmOutput: TMemo;

private

{ Private declarations } public

{ Public declarations } end;

Как мы увидим дальше, классы служат основным инструментом реализации мощных возможностей Delphi. Класс является образцом, по которому создаются объекты, и наоборот, объект - это экземпляр реализации класса. Образцы для создания элементов программы в Object Pascal называются типами, таким образом, класс TfmExamplel -это тип. Перед его объявлением стоит зарезервированное слово type (тип), извещающее компилятор о начале раздела описания типов.

Стандартный класс TForm реализует все нужное для создания и функционирования пустого Windows-окна. Класс TfmExamplel порожден от этого класса, о чем свидетельствует строка

TfmExample = class(TForm)

в которой за зарезервированным словом class в скобках указывается имя родительского класса. Термин “порожден” означает, что класс TfmExample унаследовал все возможности родительского класса TForm и добавил к ним собственные в виде дополнительных компонентов, которые, как вы помните, мы вставили в форму fmExample. Перечень вставленных нами компонентов и составляет значительную часть описания класса.

Свойство наследования классами-потомками всех свойств родительского класса и обогащения их новыми возможностями является одним из фундаментальных принципов объектно-ориентированного программирования. От наследника может быть порожден новый наследник, который внесет свою лепту в виде дополнительных программных заготовок и т. д. В результате создается ветвящаяся иерархия классов, на вершине которой располагается самый простой класс TObject (все остальные классы в Delphi порождены от этого единственного прародителя), а на самой нижней ступени иерархии - мощные классы-потомки, которым по плечу решение любых проблем.

Объект fmExampie формально относится к элементам программы, которые называются переменными. Вот почему перед объявлением объекта стоит зарезервированное слово var (от англ. variables - переменные).

Текст модуля доступен как Delphi ,так и программисту. Delphi автоматически вставляет в текст модуля описание любого добавленного к? форме компонента, а также создает заготовки для обработчиков событии; программист может добавлять свои методы в ранее объявлённые классыйнаполвять обработчики событий конкретным .содержа,нием, вставлять собственные переменные, типы, константы и т. д. Совместное с Delphi владение текстом модуля будет вполне успешным, если программист будет соблюдать простое правило , он не должен удалять или изменять строки которые вставлены не им, а Delphi.

5.2.3. Элементы программы

Элементы программы - это минимальные неделимые ее части, еще несущие в себе определенную значимость для компилятора. К элементам относятся:

Зарезервированные слова это английские слова, указывающие компилятору на необходимость выполнения определенных действий. Зарезервированные слова не могут использоваться в программе ни для каких иных целей кроме тех, для которых они предназначены. Например, зарезервированное слово begin означает для компилятора начало составного оператора. Программист не может создать в программе переменную с именем begin, константу begin, метку begin или вообще какой бы то ни было другой элемент программы с именем begin.

Идентификаторы - это слова, которыми программист обозначает любой другой элемент программы, кроме зарезервированного слова, идентификатора или комментария. Идентификаторы в Object Pascal могут состоять из латинских букв, арабских цифр и знака подчеркивания. Никакие другие символы или специальные знаки не могут входить в идентификатор. Из этого простого правила следует, что идентификаторы не могут состоять из нескольких слов (нельзя использовать пробел) или включать в себя символы кириллицы (русского алфавита).

Типы - это специальные конструкции языка, которые рассматриваются компилятором как образцы для создания других элементов программы, таких как переменные, константы и функции. Любой тип определяет две важные для компилятора вещи: объем памяти, выделяемый для размещения элемента (константы, переменной или результата, возвращаемого функцией), и набор допустимых действий, которые программист может совершать над элементами данного типа. Замечу, что любой определяемый программистом идентификатор должен быть описан в разделе описаний (перед началом исполняемых операторов). Это означает, что компилятор должен знать тот тип (образец), по которому создается определяемый идентификатором элемент.

Константы определяют области памяти, которые не могут изменять своего значения в ходе работы программы. Как и любые другие элементы программы, константы могут иметь свои собственные имена. Объявлению имен констант должно предшествовать зарезервированное слово const (от англ. constants - константы). Например, мы можем определить константы const

Kbyte = 1024;

Mbyte = Kbyte*Kbyte;

Gbyte = 1024*Mbyte;

чтобы вместо длинных чисел

1048576 (1024*1024) и 1073741824

(1024*1024*1024) писать, соответственно, Mbyte и Gbyte. Тип константы определяется способом ее записи и легко распознается компилятором в тексте программы, поэтому программист может не использовать именованные константы (т. е. не объявлять их в программе явно).

Переменные связаны с изменяемыми областями памяти, т. е. с такими ее участками, содержимое которых будет меняться в ходе работы программы. В отличие от констант переменные всегда объявляются в программе. Для этого после идентификатора переменной ставится двоеточие и имя типа, по образу которого должна строиться переменная. Разделу объявления переменной (переменных) должно предшествовать слово var. Например:

var

inValue: Integer;

byValue: Byte;

Здесь идентификатор inValue объявляется как переменная типа integer, а идентификатор byValue - как переменная типа Byte. Стандартный (т. е. заранее определенный в Object Pascal) тип integer определяет четырехбайтный участок памяти, содержимое которого рассматривается как целое число в диапазоне от -2 147 483 648 до+2 147 483 647, а стандартный тип Byte - участок памяти длиной 1 байт, в котором размещается беззнаковое целое число в диапазоне от 0 до 2554.[Все приводимые в книге сведения относительно диапазона возможных значений и объема памяти стандартных типов относятся к Delphi 32. Для 16-разрядной версии 1 эти величины имеют другие значения, например, тип Integer в версии 1 занимает 2 банта и имеет диапазон значении от -32 768 до+32 767.]

Метки - это имена операторов программы. Метки используются очень редко и только для того, чтобы программист смог указать компилятору, какой оператор программы должен выполнятся следующим. Метки, как и переменные, всегда объявляются в программе. Разделу объявлений меток предшествует зарезервированное сло-во label (метка). Например:

label

Loop;

begin

Goto Loop;

// Программист требует передать управление

// оператору, помеченному меткой Loop. .....

// Эти операторы будут пропущены

Loор:

// Оператору, идущему за этой меткой,

.....

// будет передано управление

end;

Подпрограммы - это специальным образом оформленные фрагменты программы. Замечательной особенностью подпрограмм является их значительная независимость от остального текста программы. Говорят, что свойства подпрограммы локализуются в ее теле. Это означает, что, если программист что-либо изменит в подпрограмме, ему, как правило, не понадобится в связи с этим изменять что-либо вне подпрограммы. Таким образом, подпрограммы являются средством структурирования программ, т. е. расчленения программ на ряд во многом независимых фрагментов. Структурирование неизбежно для крупных программных проектов, поэтому подпрограммы используются в Delphi-программах очень часто.

В Object Pascal есть два сорта подпрограмм: процедуры и функции. Функция отличается от процедуры только тем, что ее идентификатор можно наряду с константами и переменными использовать в выражениях, т. к. функция имеет выходной результат определенного типа. Если, например, определена функция

Function MyFunction: Integer;

и переменная var

X: Integer;

то возможен такой оператор присваивания:

Х := 2*MyFunction-l;

Имя процедуры нельзя использовать в выражении, т. к. процедура не имеет связанного с нею результата:

Procedure MyProcedure;

X := 2*MyProcedure-l; // Ошибка!

5.3. ТИПЫ

Типы в Object Pascal играют огромную роль. Связано это с тем, что лежащий в его основе язык Pascal был специально придуман как средство обучения студентов программированию. Поскольку начинающий программист легко может допустить ошибку или неточно описать свои действия, компилятор Pascal должен был иметь средства контроля за действиями программиста, чтобы вовремя предостеречь его от последствий неверных действий. Первоначально типы как раз и предназначались для того, чтобы программист явно указывал компилятору, какого размера память нужна ему в программе и что он собирается делать с этой памятью. Практика применения типов показала их высокую эффективность для защиты программы от случайных ошибок, так что практически все современные языки программирования в той или иной степени реализуют механизм типов. В Object Pascal значение типов возросло в еще большей степени, т. к. с их помощью определяются классы - основной инструмент программиста.

5.3.1. Строковый и символьный типы

Свое знакомство с типами мы начнем со строкового типа String. Этот тип определяет участок памяти переменной длины, каждый байт которого содержит один символ. Для символов в Object Pascal используется тип Char, таким образом, String - это цепочка следующих друг за другом символов Char. Каждый символ в String пронумерован, причем первый символ имеет номер 1. Программист может обращаться к любому символу строки, указывая его порядковый номер в квадратных скобках сразу за именем переменной:

var // Начало раздела описания переменных

S: String;

// Объявление переменной строкового типа

begin

// Начало раздела исполняемых операторов

S := 'Строка символов';

// Переменная S содержит

// значение ”Строка символов”

S[6] := 'и'; // Теперь переменная содержит значение

// ”Строки символов”

end;

// Конец раздела исполняемых операторов

Наличие комментариев избавляет меня от необходимости подробного описания назначения каждой строки текста. Напомню, что программист обязан объявить любой вводимый им идентификатор. Идентификатор s неизвестен Delphi - он введен нами для переменной строкового типа. После его объявления в разделе переменных Delphi выделит для него начальную область памяти минимальной длины и будет контролировать использование этого идентификатора в разделе исполняемых операторов. Например, если в этом разделе встретится выражение

2*S-1

компилятор сразу забьет тревогу, так как строка не может быть участником математических вычислений: она предназначена для хранения символов, а не чисел.

В первом операторе присваивания в переменную s будет помещено значение строковой константы строка символов'. Строковые константы содержат произвольные символы, заключенные в обрамляющие апострофы, причем сами апострофы не входят в значение константы, поэтому после присваивания переменная примет значение Строка символов без апострофов (если понадобится включить в текстовую константу апостроф, он удваивается: ' символ ' ' - апостроф'). После первого присваивания s будет занимать участок памяти длиной 15 байт - по одному байту на каждый символ значения[ На самом деле переменная строкового типа всегда имеет длину 4 байта, которые используются для указания на область памяти, в которой помещается сама строка символов. Подробнее о механизме использования памяти строковыми переменными см. п. 6.3.1.]. Переменность размера области памяти, выделяемой для размещения строки символов, - характерная особенность типа string. Если бы, например, во втором операторе мы обратились не к 6-му по счету символу, а ко всей строке в целом:

S := 'и';

эта переменная стала бы занимать 1 байт, а следующие за ним 14 байтов оказались бы свободными. Длина строковой переменной в программе меняется автоматически при каждом присваивании переменной нового значения и может составлять от 0 до 2 Гбайт[Для версии Delphi 1 тип String соответствует “короткой” строке, длина которой не может превышать 255 байт.] (1 Гигабайт (Гбайт) соответствует константе Gbyte на стр. 84). Над строковым типом определена операция сцепления (+):

S := 'Object'+' Pascal';// S содержит “Object Pascal”

 

Кроме того, строки можно сравнивать с помощью таких операций отношения:

Операция

Смысл

=

Равно

<>

Не равно

>

Больше

>=

Больше или равно

<

Меньше

<=

Меньше или равно

Примечание. Символы <>, <= и >= пишутся слитно, их нельзя разделять пробелами иди комментариями.

Результат применения операции сравнения к двум строкам имеет логический тип, который характеризуется двумя возможными значениям: True (Истина; читается тру) и False (Ложь; читается фоле). Строки сравниваются побайтно, слева направо; каждая пара символов сравнивается в соответствии с их внутренней кодировкой (см. гл. 7).

Все остальные действия над строками осуществляются с помощью нескольких стандартных для Delphi подпрограмм, описанных в гл.7.

Учебная программа COPY.TXT

Создадим программу, в которой текст из строки ввода edinput будет копироваться без изменений в метку lboutput и редактор mmoutput. Для этого загрузите Delphi и выберите опцию File | New | Application [Как уже говорилось в гл. 2, имеет смысл для каждой учебной программы создавать свой подкаталог и размещать в нем все связанные с программой файлы. Если вы еще не сделали этого, минимизируйте Delphi и создайте с помощью Проводника подкаталог COPYTXT в каталоге для ваших учебных программ. В дальнейшем я не буду напоминать вам о необходимости создания подкаталогов, однако помните, что, если вы не будете этого делать, очень скоро ваш учебный каталог станет представлять собой хаотическое нагромождение никак не связанных друг с другом файлов.].

Чтобы сообщить программе о том, что в строке ввода edinput подготовлен текст, пользователь должен щелкнуть по кнопке bbRun, поэтому все необходимые действия мы сосредоточим в обработчике события onciick этой кнопки. Дважды щелкните по кнопке bbRun в окне формы, чтобы Delphi автоматически подготовила для вас заголовок обработчика события, и отредактируйте его следующим образом: 

procedure TfmExample.bbRunClick(Sender: TObject);

begin

lbOutput.Caption := edInput.Text;

// Повторяем текст в метке mmOutput.Lines.Add(edInput.Text);

// и в многострочном редакторе

edInput.Text := '';

// Очищаем строку ввода edInput.SetFocus;

// Передаем ей фокус ввода

end;

Нажмите F9 для прогона программы, сохраните модуль под именем CopyTxtU.pas, а проект в целом - под именем copyTxt.dpr, и вы увидите окно, показанное на рис. 5.4.

Рис. 5.4. Окно программы CopyTxt

 Комментарий к программе

Вводимый текст хранится в свойстве Text компонента edInput. После выполнения первого оператора присваивания он будет перенесен в свойство caption компонента IbOutput и тут же появится на экране над строкой ввода.

Многострочньш редактор mmOutput способен сохранять и отображать на экране множество строк. Все они содержатся в его свойстве Lines, относящемуся к классу TStringList (подробнее об этом классе см. п. 12.1). Метод Add класса TStringList добавляет новую строку к имеющемуся в Lines набору строк. Добавленная с помощью второго оператора строка тут же отображается на экране. С помощью оператора

edInput.Text := ' ' ;

строка ввода очищается и подготавливается к вводу нового текста. Двойной апостроф в правой части оператора указывает “пустую” строку или, что то же, строку нулевой длины. Завершает обработчик оператор

edinput.SetFocus ;

с помощью которого строка edinput получает фокус ввода. Термином “фокус ввода” в Delphi фактически обозначается клавиатура”* “Получить фокус ввода” означает связать указанный компонент с клавиатурой. В окне программы может быть несколько компонентов, способных принимать и обрабатывать клавиатурный ввод (в нашей программе таких компонентов четыре: редактор mmoutput, строка edinput и кнопки bbRun и bbciose). Каждый из таких компонентов имеет метод SetFocus, с помощью которого программа может передать компоненту контроль над клавиатурой. Чтобы оценить действия, реализуемые оператором edinput. SetFocus, поставьте в самом начале оператора признак комментария (символы “//”), сделайте новый прогон программы и введите с ее помощью хотя бы 2-3 строки.

Замечу, что в момент появления окна программы на экране строка edinput не имеет фокуса ввода, что затрудняет ввод первой строки. Попробуйте самостоятельно создать обработчик события опАс-tivate для формы fmExampie и в нем установить фокус ввода для строки edinput. (Подсказка: раскройте список выбора в верхней части окна Инспектора объектов, выберите в нем компонент fmExampie и дважды щелкните по правой колонке свойства OnActivate на странице Events этого окна.)

5.3.2. Целые типы

Целые типы используются для хранения и преобразования целых чисел. В Object Pascal предусмотрено несколько целочисленных типов, отличающихся диапазоном возможных значений. В этой главе мы будем использовать тип integer, занимающий в памяти 4 смежных байта и предназначенный для хранения целых чисел в диапазоне от -2 147 483 648 до +2 147 483 6478. Более полные сведения о целых типах вы найдете в гл. 7.

Над целыми числами определены следующие математические операции:

+

+ сложение

-

вычитание

*

умножение

div

деление с отбрасыванием остатка

mod

получение остатка от деления

Спецификой деления является то обстоятельство, что результат может иметь дробный вид: 1/2, 2*3/5 и т. п. Для хранения дробных чисел в Object Pascal используются вещественные типы (см. п. 7.3.3), вот почему в языке имеются целых две операции деления (div и mod):

var

X,Y: Integer;

begin

X := 5 div 2; // X содержит 2

Y := 5 mod 2; // Y содержит 1

end;

Смысл остальных операций совпадает с общепринятым математическим, за тем исключением, что результат не должен выходить из диапазона возможных значений целого типа:

var

X: Integer;

begin

X := 2147483647;

X := X + 1; // Ошибка! Результат +2 147 483 648 выходит

// из допустимого диапазона.

end;

Как и к строкам, к целым числам применимы операции сравнения.

Учебная программа INTMULT

В описываемой ниже программе пользователь вводит два целых числа, а программа вычисляет и показывает их произведение. В этой программе мы также познакомимся с преобразованием типов. На рис. 5.5. показан вид окна работающей программы.[В версии Delphi 1 тип Integer занимает 2 байта и содержит число в диапазоне от -32 768 до +32 767.]

 

Рис. 5.5. Окно программы IntMult

Прежде всего обсудим две проблемы, связанные с реализацией программы:

  1. как известить программу о вводе каждого из операндов;
  2. что будет, если пользователь подготовит неверную запись целого числа (например “12.3”).

Суть первой проблемы состоит в том, что подготовленная нами учебная форма fmExampie имеет только одну строку ввода edinput и одну кнопку bbRun (кнопка bbClose используется для прекращения работы программы и эту ее функциональность не следует изменять). Мы могли бы написать программу, которая вводит строку типа

2*3

и после анализа строки выводит результат. Однако логика анализа существенно перегрузит программу и потребует использования в ней средств, с которыми мы пока не знакомы. Значительно проще вставить в форму еще одну кнопку. Тогда щелчок по первой кнопке будет сигналом о том, что в строке ввода подготовлен первый операнд, а щелчок по второй - что готов второй операнд и можно вычислять результат. В этом варианте следует предусмотреть случай, когда щелчок по второй кнопке будет предшествовать щелчку по первой и программа будет вычислять выражение с неопределенным первым операндом. Решение этой дополнительной проблемы состоит в том, чтобы сделать вторую кнопку недоступной или даже невидимой до тех пор, пока не будет введен первый операнд.

Подводя итог, я предлагаю поместить на форму fmExample новую кнопку BitBtn (страница Additional галереи компонентов) так, чтобы она полностью закрыла собой имеющуюся кнопку bbRun, назвать новую кнопку bbResuit (свойство Name), установить для нее сорт (свойство Kind) bkOK и сделать ее временно невидимой (поместить в свойство visible значение False). После такой подготовки в момент начала работы новая кнопка не будет видна и не помешает пользователю щелкнуть по кнопке bbRun. В обработчике события bbRun-click следует спрятать кнопку bbRun и показать кнопку bbResuit, чтобы пользователь смог подтвердить ввод второго операнда. Сделайте необходимые изменения формы для нового проекта, а я пока перейду к обсуждению второй проблемы.

Суть второй проблемы заключается в том, что пользователь может случайно или преднамеренно ввести строку, которая не содержит правильного представления целого числа. Преобразование такой строки к целому числу может привести к аварийному завершению работы программы. Существует множество способов решения этой проблемы, я предлагаю наиболее простой: заменить обычный однострочный редактор edinput на специализированный, способный контролировать ввод пользователя. Удалите из формы редактор edinput (щелкните по нему мышкой и нажмите клавишу Delete) и поместите на его место компонент MaskEdit (страница Additional). Дайте ему прежнее имя edinput (свойство Name) и раскройте диалоговое окно свойства EditMask (рис. 5.6). Это свойство указывает маску компонента, т. е. определяет, какие символы и в какой последовательности может набирать пользователь в окне ввода.

Рис. 5.6. Окно определения маски компонента MaskEdit

В строке Input Mask введите

#99999999;1;

В окошке Character for Blanks вместо умалчиваемого символа-заполнителя “_” укажите пробел и закройте окно кнопкой ок. Сформированная нами маска заставит компонент автоматически контролировать ввод пользователя и отвергать любые символы, кроме знаков + или - в первой позиции (элемент маски “#”) и цифр (элементы “9”).

Теперь все готово к кодированию программы.

Дважды щелкните по кнопке bbResuit и введите такой код для обработчика события OnClick этой кнопки:

procedure TfniExample.bbResultClick( Sender: TObject) ;

// Обработка ввода 2-го операнда

begin // Преобразуем текст из edinput в целое число:

Y := StrToInt(Trim(edinput.Text));

// Сообщаем в Memo о вводе 2-го операнда:

mmOutput.Lines.Add('2-й операнд:'+edInput.Text);

// Вычисляем и показываем результат:

mmOutput.Lines.Add('Результат: '+ IntToStr(X)+' * '+IntToStr(Y)+' = '+IntToStr(X*Y));

edinput.Text := ''; // Очищаем ввод

edinput. SetFocus;

// и возвращаем ему фокус

IbOutput.Caption := 'Введите 1-й операнд; bbResuit.Hide;

// Прячем кнопку

bbResuit bbRun.Show;

// и показываем bbRun

end;

Для ввода обработчика события onciick кнопки bbRun нужно сначала с помощью списка выбора в верхней части окна Инспектора объектов отыскать и выбрать компонент bbRun (напомню, он полностью закрыт кнопкой bbResuit), затем дважды щелкнуть в правом столбце строки onciick на странице Events и ввести такой код:

procedure TfmExample.bbRunClick(Sender: TObject);

// Обработка ввода 1-го операнда

begin

// Преобразуем текст из edinput в целое число

Х := StrToInt(Trim(edinput.Text));

// Сообщаем в Memo о вводе 1-го операнда

mmOutput.Lines.Add('1-й операнд: '+edInput.Text);

edinput.Text := ''; // Очищаем ввод

edInput.SetFocus;// и возвращаем ему фокус

IbOutput.Caption := 'Введите 2-й операнд:';

bbResult.Show; // Показываем кнопку bbResult

bbRun.Hide; // и прячем bbRun

end;

Осталось передать фокус ввода компоненту edinput в момент старта программы и определить переменные х и y для хранения операндов. Разыщите в самом начале окна кода программы описание класса TfmExample и отредактируйте его следующим образом:

private

{ Private declarations } X,Y: Integer;.

Затем с помощью списка выбора в окне Инспектора объектов выберите форму fmExampie и после двойного щелчка на строке опАс-tivate страницы Events введите такой обработчик события:

procedure TfmExample.FormActivate(Sender: TObject);

// Подготовка к началу работы

begin

edInput.SetFocus; // Передаем фокус ввода редактору edinput

IbOutput.Caption := 'Введите 1-й операнд:';

end;

Комментарий к программе

С помощью строк

private

{ Private declarations }

X,Y: Integer;

мы определили в классе TfmExample две целочисленные переменные Х и Y, которые после этого стали доступны любым методам этого класса, в том числе обработчикам событий OnClick. Такого рода переменные (их называют полями класса) облегчают взаимодействие методов друг с другом. В нашем случае необходимость в них возникла по той причине, что первый операнд (переменная X) вводится в одном методе (bbRunciick), а используется в другом

(bbResultClick).

Оба обработчика событий onciick содержат почти одинаковые операторы. В начале каждого из них стоит оператор присваивания, в правой части которого содержится выражение

StrToInt(Trim(edinput.Text))

Это выражение состоит из вызова стандартных для Object Pascal функций: сначала вызывается функция Trim , которая возвращает строку-аргумент (в нашем случае edinput.Text) без обрамляющих ее (т. е. ведущих и ведомых) пробелов9; затем с помощью вызова StrToint строка символов преобразуется к целому числу.

Функция преобразования StrToint очень чувствительна к возможным ошибкам символьного представления целого числа. Большая часть возможных ошибок в нашей программе блокируется фильтрующими возможностями редактора MaskEdit и функцией Trim. Программа аварийно завершится, если при щелчке по кнопкам bbRun или bbResuit редактор не содержит текста. Чтобы избежать этого, перед вызовом StrToint вставьте

if edinput.Text =''' then Exit;

Этот оператор прекращает дальнейшую работу обработчика в случае, если в окне ввода компонента edinput нет текста.

Методы Show и Hide компонентов-кнопок соответственно показывают или прячут компонент. Спрятанный компонент никак не участвует во взаимодействии с пользователем.

5.4. ОПЕРАТОРЫ ЯЗЫКА

С одним из наиболее часто используемых операторов языка Object Pascal - оператором присваивания мы уже познакомились (см. п. 3.3.3). Ниже рассматриваются остальные операторы.

5.4.1. Составной оператор и пустой оператор

Составной оператор - это последовательность произвольных операторов программы, заключенная в операторные скобки - зарезервированные слова begin ... end. Составные операторы - важный инструмент Object Pascal, дающий возможность писать программы по современной технологии структурного программирования (без операторов перехода goto).

Object Pascal не накладывает никаких ограничений на характер операторов, входящих в составной оператор. Среди них могут быть и другие составные операторы - язык Object Pascal допускает произвольную глубину их вложенности:

Функция Trim не определена в версии 1. Вместо указанной единственной строки в этом случае

следует написать:[while post' ', edinput.Text)>0 do

system.delete(edinput.Text, post' ', edinput.Text), 1);

StrToint(edinput.Text);]

begin

begin

begin

end;

end;

end;

Фактически весь раздел операторов, обрамленный словами begin ... end, представляет собой один составной оператор. Поскольку зарезервированное слово end является закрывающей операторной скобкой, оно одновременно указывает и конец предыдущего оператора, поэтому ставить перед ним символ “;” необязательно. Наличие точки с запятой перед end в предыдущих примерах означало, что между последним оператором и операторной скобкой end располагается пустой оператор. Пустой оператор не содержит никаких действий, просто в программу добавляется лишняя точка с запятой. В основном пустой оператор используется для передачи управления в конец составного оператора: как и любой другой, пустой оператор может быть помечен, и ему можно передать управление.

5.4.2. Условный оператор

Условный оператор позволяет проверить некоторое условие и в зависимости от результатов проверки выполнить то или иное действие. Таким образом, условный оператор - это средство ветвления вычислительного процесса.

Структура условного оператора имеет следующий вид:

if <условие> then <оператор1> else <оператор2>;

где if/ then/ else - зарезервированные слова (если, то, иначе);

<условие> - произвольное выражение логического типа;

<оператор1>, <оператор2> - любые операторы языка Object Pascal.

Условный оператор работает по следующему алгоритму. Вначале вычисляется условное выражение <условие>. Если результат есть True (истина), то выполняется <оператор1>, а <оператор2> пропускается; если результат есть False (ложь), наоборот, <оператор1> пропускается, а выполняется <оператор2>. Например:

var

X, Y, Max: Integer;

begin .

if X > Max then

Y := Max else

Y := X;

….

end;

При выполнении этого фрагмента переменная y получит значение переменной х, если только это значение не превышает мах, в противном случае y станет равно мах.

Условными называются выражения, имеющие одно из двух возможных значений: истина или ложь. Такие выражения чаще всего получаются при сравнении переменных с помощью операций отношения =, <>, >, >=, <, <=. Сложные логические выражения составляются с использованием логических операций and (логическое И), or (логическое ИЛИ) и not (логическое НЕ). Например:

if (а > b) and (b <> 0) then ...

В отличие от других языков программирования в Object Pascal приоритет операций отношения меньше, чем у логинеёкйх" операции, по этому отдельные составные части сложного логического вьфажёния5 заключаются в скобки. Например, такая запись предыдущего оператора будет неверной:

if a>b and b <> 0 then ...// Ошибка так как фактически (с учетом приоритета операции) компилятор будет транслировать такую строку:

if a>(b and b)<>0 then...

Часть else <оператор2> условного оператора может быть опущена. Тогда при значении True условного выражения выполняется <оператор1>, в противном случае этот оператор пропускается:

var

X, Y, Max: Integer;

begin

if X > Мах then Мах := X;

Y := X;

end;

В этом примере переменная y всегда будет иметь значение переменной х, а в мах запоминается максимальное значение X.

Поскольку любой из операторов <оператор1> и <оператор2> может быть любого типа, в том числе и условным, а в то же время не каждый из “вложенных” условных операторов может иметь часть else <оператор2>, то возникает неоднозначность трактовки условий. Эта неоднозначность в Object Pascal решается следующим образом:

любая встретившаяся часть else соответствует ближайшей к ней сверху по тексту программы части then условного оператора. Например:

var

a,b,c,d : Integer;

begin a := 1;

b := 2;

с := 3;

d := 4;

if a < b then // Да

if с < d then // Да

if с < 0 then // Нет

с := 0 // Этот оператор не выполняется

else

а := b; // а равно 2

if а < b then // Да

if с < d then // Да

if с < 0 then // Нет

с := 0 // с равно 0

else //if с < О else //if с < d

else // If a < b

а := b; // Этот оператор не выполняется

end;

 

Учебная программа INTTEST

Игра “Угадай число”: программа случайным образом выбирает целое число в диапазоне О... 1000 и запоминает его. Пользователь пытается угадать его и вводит свое число. Сравнив ввод с запомненным числом, программа сообщает - больше, меньше или равно введенное число запомненному. Ввод продолжается до угадывания, после чего программа предлагает сыграть еще раз.

Поскольку пользователь вводит только числа, изменим форму fmExample: как и в предыдущей программе intmult вместо компонента edinput типа TEdit поместим на форму одноименный компонент типа TMaskEdit. Выберите опцию File | New | Application, щелкните по компоненту edinput и удалите его с помощью клавиши Delete, затем поместите на его место компонент MaskEdit (страница Additional), назовите его edInput (свойство Name) и в его свойство

EditMask поместите строку

0999;1;

В секцию private описания класса fmExampie поместите объявление целочисленного поля х:

private

{ Private declarations }

X: Integer;

Для обработчика события OnActivate формы fmExampie Напишите такой код:

procedure TfmExample.FormActivate(Sender: TObject);

begin

X := Random (1001) ; // Загадываем случайное число

edinput.SetFocus; // Передаем строке edinput фокус ввода

Caption := 'Угадай целое число в диапазоне О...1000';

IbOutput.Caption :== 'Введите число:';

end;

Поместите в обработчик события onciick кнопки bbRun такой код:

procedure TfmExample.bbRunClick(Sender: TObject);

var

Y: Integer;

begin

if edinput.Text = '' then

Exit; // Если нет входного текста, прекращаем работу

// Преобразуем ввод в число:

Y := StrToInt(Trim(edInput.Text));

edinput.Text := ''; // Очищаем ввод

edinput.SetFocus; // Устанавливаем фокус ввода

{ Параметр Tag = 0 означает угадывание числа, иначе - ответ на вопрос, будет ли пользователь играть после угадывания }

if Tag = 0 then

if X < Y then // Угадывание числа

mmOutput.Lines.Add('X < '+IntToStr(Y))

else if X > Y then

mmOutput.Lines.Add('X > '+IntToStr (Y))

else // X = Y begin

mmOutput.Lines.Add("X = +IntToStr(Y));

MessageBeep(MB_OK); // Звук для привлечения внимания

IbOutput.Caption :='Вы угадали! Введите 1, если хотите повторить:';

Tag := 1; // Следующий ввод - ответ на вопрос

end;

else // Анализ ответа

if Y = 1 then

begin

X := Random(1001); // Новое число

IbOutput.Caption := 'Введите число:';

Tag := 0; // Следующий ввод - угадывание

edinput.SetFocus

end

else

Close;

end;

Комментарий к программе

При активизации окна программы обработчик FormActivate с помощью оператора

Х := Random(1001) ;

помещает в поле X случайное целое число в диапазоне от 0 до 1001 -1 = 1000 (параметр обращения к генератору псевдослучайных чисел Random на единицу больше верхней границы диапазона).

Каждый компонент Delphi имеет целочисленное свойство Tag, которым программист может распоряжаться по своему усмотрению. Программа использует это свойство формы fmExampie для разделения алгоритма обработки события OnClick кнопки bbRun на два варианта: пользователь угадывает число (тад = 0) или отвечает на предложение сыграть еще раз (тад = 1). В момент создания экземпляра класса TfmExmple - объекта fmExample - все его поля обнуляются, поэтому первоначально Tag = 0 и программа анализирует угадывание числа. При угадывании срабатывает составной оператор

begin

mmOutput.Lines.Add('X = Ч-IntToStr(Y)) ;

MessageBeep(MB OK); // Звук для привлечения внимания

IbOutput.Caption :='Вы угадали! Введите 1, если хотите повторить:';

Tag := 1; // Следующий ввод - ответ на вопрос

end

и Tag получает значение 1 (стандартная процедура MessageBeep заставляет звуковую систему ПК издать короткий звуковой сигнал).

Теперь очередной щелчок по кнопке bbRun будет означать, что пользователь подготовил ответ на вопрос, и программа анализирует его. Если пользователь ввел число отличное от 1, произойдет обращение к методу close формы fmExample и программа прекратит работу.

5.4.3. Операторы повторений

В языке Object Pascal имеются три различных оператора, с помощью которых можно запрограммировать повторяющиеся фрагменты программ.

Счетный оператор цикла FOR имеет такую структуру:

for <параметр цикла> := <нач_знач> to <кон знач> do <оператор>;

Здесь for, to, do - зарезервированные слова (для, до, выполнить);

<параметр_цикла> - переменная типа Integer (точнее, любого по-, рядкового типа, см. гл. 7); <нач_знач> - начальное значение - выражение того же типа; <кон_знач> - конечное значение - выражение того же типа; <оператор> - произвольный оператор Object Pascal.

При выполнении оператора for вначале вычисляется выражение <нач_знач> и осуществляется присваивание <параметр_цикла> := <нач_знач>. После этого циклически повторяется:

Учебная программа INTSLJMM

В качестве иллюстрации применения оператора for рассмотрим программу, осуществляющую ввод произвольного целого числа n и вычисление суммы всех целых чисел от 1 до N.

Для нового приложения (опция File | New | Application) создайте такой обработчик bbRunСlick:

procedure TfmExample.bbRunClick(Sender: TObject);

var

i,N,Sum : Integer;

begin

try // Преобразуем ввод с контролем правильности:

N := StrToInt(edInput.Text);

except // Следующие операторы выполняются, если есть ошибка ShowMessage('Ошибка ввода целого числа');

dinput.SelectAll; // Выделяем неверный ввод

Exit // Завершаем работу обработчика

end;

edInput.Text :=' ';

edinput.SetFocus;

Sum := 0; // Начальное значение Sum

for i := 1 to N

do // Цикл формирования суммы

Sum := Sum+i;

mmOutput.Lines.Add('Сумма всех целых чисел '+'в диапазоне 1...'+IntToStr(N)+' равна '+IntToStr(Sum));

end;

Комментарий к программе

Прежде всего обратите внимание на операторы

try // Преобразуем ввод с контролем правильности:

N := StrToInt(edinput.Text);

except // Следующие операторы выполняются, если есть ошибка ShowMessage("Ошибка ввода целого числа');

edinput.SelectAll; // Выделяем неверный ввод

Exit // Завершаем работу обработчика

end;

С помощью зарезервированных слов try (попробовать), except (исключение) и end реализуется так называемый защищенный блок. Такими блоками программист может защитить программу от краха при выполнении потенциально опасного участка (подробнее см. п. 14.1). В отличие от предыдущих программ мы не изменили компонент edinput, поэтому пользователь может ввести в нем произвольный текст. Если этот текст не содержит правильное представление целого числа, попытка выполнить оператор

N := StrToInt(edInput.Text);

в обычной программе привела бы к аварийному завершению программы. Чтобы этого не произошло, мы защитили этот оператор, расположив его за try и перед except. Если ошибки нет, все операторы, стоящие за except и до end, пропускаются и обработчик нормально срабатывает. Если обнаружена ошибка, возникает так называемая исключительная ситуация (исключение) и управление автоматически передается оператору, стоящему за except, - начинается обработка исключения. Вначале с помощью стандартной процедуры ShowMessage мы сообщаем пользователю об ошибке[Если вы запустите программу из среды Delphi, исключение будет сначала перехвачено средой и на экране появится сообщение на английском языке о характере и месте возникновения ошибки. В этом случае закройте окошко с сообщением и нажмите F9 - программа продолжит свою работу, и вы увидите окно процедуры ShowMessage.], затем с помощью edInput. SeiectAll выделяем ошибочный текст в компоненте edinput и, наконец, с помощью вызова стандартной процедуры Exit аварийно завершаем работу обработчика (но не программы!).

Отметим также два обстоятельства. Во-первых, условие, управляющее работой оператора for, проверяется перед выполнением оператора <оператор>: если условие не выполняется в самом начале работы оператора for, исполняемый оператор не будет выполнен ни разу. Другое обстоятельство - шаг наращивания параметра цикла строго постоянен и равен (+1). Существует другая форма оператора:

for <пар_цик>: = <нач_знач>downto <кон_знач>do <оператор>;

Замена зарезервированного слова to на downto означает, что шаг наращивания параметра цикла равен (-1), а управляющее условие Приобретает вид <параметр__цикла> = <кон_знач>.

Предыдущий пример можно модифицировать так, чтобы сделать его пригодным для подсчета любых сумм - положительных и отрицательных:

Sum := 0;

if N >= 0 then

for i := 1 to N do

Sum := Sum + i

else

for i := -1 downto N do

Sum := Sum + i ;

Два других оператора повторений лишь проверяют условие выполнения или повторения цикла, но не связаны с изменением счетчика цикла.

Оператор цикла WHILE с предпроверкой условия:

while <условие> do <оператор>;

Здесь while, do - зарезервированные слова {пока [выполняется условие], делать), <условие> - выражение логического типа; <оператор> - произвольный оператор Object Pascal.

Если выражение <условие> имеет значение True, то выполняется <оператор>, после чего вычисление выражения <условие> и его проверка повторяются. Если <условие> имеет значение False, оператор while прекращает свою работу.

Учебная программа EPSILON

Программа отыскивает так называемое “машинное эпсилон” -такое минимальное, не равное нулю вещественное число, которое после прибавления его к 1,0 еще дает результат, отличный от 1,0. Замечу, что для хранения и преобразования дробных чисел в Object Pascal предназначены так называемые вещественные типы (см. гл. 7). В учебной программе используется один из этих типов - Real, занимающий 8 смежных байт и представляющий дробные (вещественные) числа в диапазоне от 10-324 до 10+308 с точностью 15... 16 значащих цифр10 .

У читателя, привыкшего к непрерывной вещественной арифметике, может вызвать недоумение утверждение о том, что в дискретной машинной арифметике всегда существуют такие числа o<x<eps, что i,o+x°i,o. Дело в том, что внутреннее представление типа Real может дать “лишь” приблизительно 1019 возможных комбинаций значащих разрядов в отведенных для него 8 байтах. Конечно же, это очень большое число, но оно несопоставимо с бесконечным множеством вещественных чисел. Аппроксимация бесконечного непрерывного множества вещественных чисел конечным (пусть даже и очень большим) множеством их внутреннего машинного представления, и приводит к появлению “машинного эпсилон”.

Для нового приложения (опция File I New I Application) создайте такой обработчик bbRunСlick:

procedure TfmExample.bbRunClick(Sender: TObject) ;

var

Epsilon: Real;

begin

Epsilon := 1;

while l+Epsilon/2>l do

Epsilon := Epsilon/2;

IbOutput.Caption := 'Машинное эпсилон = '+FloatToStr(Epsilon)

end;

Комментарий к программе

Для вещественных чисел можно использовать операцию деления без отбрасывания дробной части ( символ “/”). После применения этой операции результат всегда имеет вещественный тип, поэтому такой фрагмент программы ошибочен:

var

X : Integer; begin

X := 4/2; // Ошибка!Вещественный результат нельзя

// присвоить целой переменной

end;

Стандартная функция FloatToStr преобразует вещественное число в строку символов.

Оператор цикла REPEAT... UNTIL с постпроверкой условия:

repeat <тело цикла> Until <условие>;

Здесь repeat, until - зарезервированные слова (повторять [до тех пор}, пока [не будет выполнено условие]); <тело_цикла> - произвольная последовательность операторов Object Pascal; <условие> - выражение логического типа.

Операторы <тело_цикла> выполняются хотя бы один раз, после чего вычисляется выражение <условие>: если его значение есть False, операторы <тело_цикла> повторяются, в противном случае оператор repeat... until завершает свою работу.

Обратите внимание: пара repeat... unti1 подобна операторным скобкам begin ... end, поэтому перед until ставить точку с запятой необязательно.

Замечу, что для правильного выхода из цикла условие выхода должно меняться внутри операторов, составляющих тело цикла while или repeat... until. Следующие циклы никогда не завершатся “естественным” способом:

while True do begin

end;

и

repeat

until False;

Для гибкого управления циклическими операторами for, while и repeat в состав Object Pascal включены две процедуры без параметров:

break - реализует немедленный выход из цикла; действие процедуры заключается в передаче управления оператору, стоящему сразу за концом циклического оператора;

continue - обеспечивает досрочное завершение очередного прохода цикла; эквивалент передачи управления в самый конец циклического оператора.

Введение в язык этих процедур практически исключает необходимость использования операторов безусловного перехода goto (см. ниже п. 5.4.5).

5.4.4. Оператор выбора

Оператор выбора позволяет выбрать одно из нескольких возможных продолжений программы. Параметром, по которому осуществляется выбор, служит ключ выбора - выражение любого порядкового типа (из уже рассмотренных к порядковым относятся integer, char и логический; описание остальных порядковых типов см. в гл. 7).

Структура оператора выбора такова:

case <ключ_выбора> of <список_выбора> [else <операторы>] end;

Здесь case, of, else, end - зарезервированные слова (случай, из, иначе, конец); <ключ_выбора> - ключ выбора (выражение порядкового типа); <список_выбора> - одна или более конструкций вида:

<константа_выбора> : <оператор>;

<константа_выбора> - константа того же типа, что и выражение <ключ_выбора>; <оператор> - произвольный оператор Object Pascal.

Оператор выбора работает следующим образом. Вначале вычисляется значение выражения <ключ_выбора>, а затем в последовательности операторов <список_выбора> отыскивается такой, которому предшествует константа, равная вычисленному значению. Найденный оператор выполняется, после чего оператор выбора завершает свою работу. Если в списке выбора не будет найдена константа, соответствующая вычисленному значению ключа выбора, управление передается операторам, стоящим за словом else. Часть else <операторы> можно опускать. Тогда при отсутствии в списке выбора нужной константы ничего не произойдет, и оператор выбора просто завершит свою работу.

Любому из операторов списка выбора может предшествовать не одна, а несколько констант выбора, разделенных запятыми. Например:

var

ch : Char;

begin

case ch of

'n','N','H','H': IbOutput.Caption := 'Нет';

'у','Y','д','Д': IbOutput.Caption := 'Да';

end

end;

Учебная программа CALC

Составим программу, имитирующую работу микрокалькулятора. Программа вводит два операнда и знак математического действия, после чего показывает результат (см. рис. 5.7).

Для упрощения ввода операндов и знака поместим на нашу учебную форму еще два компонента. Выберите File | New | Application, переименуйте компонент ibinput в ibinputi и сделайте его свойство width (длина) равным 121 (стандартная длина компонентов Edit), a свойство Left (слева) - 64. Справа от компонента ibinputi поместите компонент comboBox (страница Standard), назовите его cbSign (свойство Name) и установите для него width =41. Раскройте диалоговое окно свойства items этого компонента и введите четыре строки со знаками математических действий в такой последовательности:

+

-

*

/

И, наконец, справа от cbSign разместите еще один компонент Edit (страница Standard) и назовите его edinput2. He забудьте очистить свойства Text у всех трех компонентов.

Рис. 5.7. Окно программы CALC

Для обработчика события bbRunciick введите такой текст:

procedure TfmExample.bbRunClick(Sender: TObject);

var

X, Y, Z: Real;

begin

{ Блокируем работу, если не введены операнды и не выбрано действие }

if (edInputl.Text='') or (edlnput2.Text='') or (cbSign.Itemlndex<0) then Exit;

// Проверяем правильность ввода 1-го операнда:

try // Начало защищенного блока

Х := StrToFloat(Trim(edinputi.Text));

except // Обработка ошибки

ShowMessage('Ошибочная запись числа: '+edlnputl.Text);

edinputi.SelectAll;

Exit;

end; // Конец защищенного блока

// Проверяем правильность ввода 2-го операнда:

try // Начало защищенного блока

Y := StrToFloat(Trim(edlnput2.Text));

except // Обработка ошибки

ShowMessage('Ошибочная запись числа: '+edlnput2.Text);

edlnput2.SelectAll ;

Exit;

end;

// Конец защищенного блока

// Все правильно: вычисляем результат

case cbSign.Itemlndex of

0: Z := X + Y;

1: Z := X - Y;

2: Z := X * Y;

3: try

Z := X / Y; except

Z := l.le+38; // Бесконечность при делении на О

end;

end;

// Показываем результат

IbOutput.Caption := Trim(edinputi.Text)+' '+ cbSign.Items[cbSign.Itemlndex]+' '+ Trim(edlnput2.Text)+' = ';

if Z >= l.le+38 then

IbOutput.Caption := IbOutput.Caption+'бесконечность'

else

IbOutput.Caption := IbOutput.Caption+FloatToStr(Z);

mmOutput.Lines.Add(IbOutput.Caption) ;

// Очищаем ввод edinputi.Text := '';

ed!nput2.Text := ' ';

cbSign.ItemIndex := -1;

end;

Комментарий к программе

В качестве операндов в программе выбраны переменные вещественного типа Real. Символьное представление значений Real в общем случае имеет вид:

±ЦЦЦЦЦ.ДДДДДе+ППП

Здесь ± - знак + или -; ццццц - цифры целой части числа; ддддд -цифры дробной части; ппп - цифры десятичного порядка. Символ е (E) в такой записи читается как “умножить на 10 в степени”. Например, 1. 1e+38 читается: “один и одна десятая умножить на 10 в степени плюс тридцать восемь”. Следует учесть, что компилятор Delphi в качестве разделителя целой и дробной частей всегда использует символ “.”, в то время как аналогичный разделитель в Windows зависит от выбранной страны, и для России им обычно является запятая “,”. Это означает, что вещественные константы в тексте программы вы всегда должны записывать с разделителем “.”, а при вводе пользователем программы вещественных чисел он должен использовать разделитель Windows (этот разделитель можно выбрать В Windows 32 С помощью Пуск | Настройка 1 Панель управления | Язык и стандарты).

Поскольку правильная форма записи вещественного числа гораздо богаче формы записи целого, использовать однострочный редактор с маской для фильтрации вещественных чисел невозможно, поэтому в программе используются обычные редакторы edInput1 и edInput2. В этой ситуации контроль за правильностью записи приходится возлагать на функцию преобразования strToFioat (эта функция преобразует строку в вещественное число). Если функция обнаружит ошибку в записи вещественного числа, она инициирует исключительную ситуацию, которая обычно приводит к аварийному завершению работы программы. Чтобы этого не произошло, используются защищенные блоки (см. комментарий к программе INITSUMM). Таким же блоком защищается операция деления: если число долится на ноль или на очень малую величину, результату присваивается большая положительная величина 1,1*10+38. При выводе сообщения эта величина заменяется на слово “бесконечность”.

Для выбора операции в программе используется компонент Combовох - раскрывающийся список выбора. Свойство items этого компонента хранит набор строк, которые определяют список выбора. Эти строки пронумерованы начиная с нуля (в нашем случае cbSign.Items[0] = '+', a cbSign. Items [3] = '/'). Номер выб-ранной строки содержит свойство cbSign.ItemIndex, а если не выбрана ни одна строка, это свойство содержит -1. Программа контролирует выбор за счет условия

cbSign.ItemIndex<0

при проверке операндов, а также в операторе case:

case cbsign.itemindex of

Если вы ввели строки cbSign. items в нужной последовательности, индекс выбранной строки будет соответствовать номеру операции (0 - +, 1- -, 2 - * и 3 - /), и над операндами будет выполнено требуемое действие.

5.4.5. Метки и операторы перехода

Можно теоретически показать, что рассмотренных операторов вполне достаточно для написания программ любой сложности. В этом отношении наличие в языке операторов перехода кажется излишним. Более того, современная технология структурного программирования основана на принципе “программировать без GOTO”: считается, что злоупотребление операторами перехода затрудняет понимание программы, делает ее запутанной и сложной в отладке. Тем не менее в некоторых случаях использование операторов перехода может упростить программу.

Оператор перехода имеет вид:

goto <метка>;

Здесь goto - зарезервированное слово (перейти [на метку]); <метка> - метка.

Метка в Object Pascal - это произвольный идентификатор, позволяющий именовать некоторый оператор программы и таким образом ссылаться на него. В целях совместимости со стандартным языком Паскаль в Object Pascal допускается в качестве меток использование также целых чисел без знака.

Метка располагается непосредственно перед помечаемым оператором и отделяется от него двоеточием. Оператор можно помечать несколькими метками, которые в этом случае отделяются друг от друга двоеточием. Перед тем как появиться в программе, метка должна быть описана. Описание меток состоит из зарезервированного слова label (метка), за которым следует список меток:

Label

loop, Ibl, lb2;

begin

goto Ibl;

 

loop: .........

Ibl:lb2: ......

......

goto lb2;

end;

Действие оператора goto состоит в передаче управления соответствующему меченному оператору.

При использовании меток необходимо руководствоваться следующими правилами:

5.5. МАССИВЫ

Рассмотренные выше простые типы данных позволяют использовать в программе одиночные объекты - числа, символы, строки и т. п. В Object Pascal могут использоваться также объекты, содержащие множество однотипных элементов. Это массивы - формальное объединение нескольких однотипных объектов (чисел, символов, строк и т. п.), рассматриваемое как единое целое. К необходимости применения массивов мы приходим всякий раз, когда требуется связать и использовать целый ряд родственных величин. Например, результаты многократных замеров температуры воздуха в течение года удобно рассматривать как совокупность вещественных чисел, объединенных в один сложный объект - массив измерений.

При описании массива необходимо указать общее количество входящих в массив элементов и тип этих элементов. Например:

var

а : array [1..10] of Real;

b : array [0..50] of Char;

с : array [-3..4] of String;

Как видим, при описании массива используются зарезервированные слова array и of (массив, из). За словом array в квадратных скобках указывается тип-диапазон, с помощью которого компилятор определяет общее количество элементов массива. Тип-диапазон (подробнее см. в гл. 7) задается левой и правой границами изменения индекса массива, так что массив А состоит из 10 элементов, массив В - из 51, а массив С - из 8 элементов. За словом of указывается тип элементов, образующих массив.

Доступ к каждому элементу массива в программе осуществляется с помощью индекса - целого числа (точнее, выражения порядкового типа, см. гл. 7), служащего своеобразным именем элемента в массиве (если левая граница типа-диапазона равна 1, индекс элемента совпадает с его порядковым номером). При упоминании в программе любого элемента массива сразу за именем массива должен следовать индекс элемента в квадратных скобках, например:

var

a: array [1..10] of Integer;

b: array [0..40] of Char;

c: array [-2..2] of Boolean;

k: Integer;

begin

b[17] := 'F';

c[-2] := a[1] > 2;

for k := 1 to 10 do a[k] := 0;

end;

В правильно составленной программе индекс не должен выходить за пределы, определенные типом-диапазоном. Например, можно использовать элементы а [ 1 ], в[38], с[0], но нельзя а [0 ] илис[38] (определение массивов см. выше). Компилятор Object Pascal может[Буквальный перевод array - боевой порядок, упорядоченная масса (войск). В компьютерной терминологии array переводится словом массив.] контролировать использование индексов в программе как на этапе компиляции, так и на этапе прогона программы.

Учебная программа AVERAGE

Для иллюстрации приемов работы с массивами составим программу, которая создает массив случайных целых чисел, подсчитывает их среднее арифметическое, а также определяет и выводит на экран минимальное и максимальное из этих чисел.

procedure TfmExample.bbRunClick(Sender: TObject);

{Программа создает массив из N случайных целых чисел, равномерно распределенных в диапазоне от 0 до MAX_VALUE-1, подсчитывает среднее арифметическое этих чисел, а также минимальное и максимальное из них.} const

N = 1000; // Количество элементов массива

MAX_VALUE = 100+1;// Диапазон значений случайных чисел var

m: array [1..N] of Integer; // Массив чисел

i: Integer;// Индекс массива

max,min: Integer; // Максимальное и минимальное число

sum: Real;// Сумма чисел

begin

// Наполняем массив случайными числами:

for i := 1 to N do

m[i] := Random(MAX_VALUE);

// Задаем начальные значения переменных:

sum : = m [ 1 ] ;

max : = m [ 1 ] ;

min := m[1] ;

// Цикл вычисления суммы всех случайных чисел и поиска

// минимального и максимального:

for i := 2 to N do

begin

sum := sum + m[i];

if m[i] < min then

min := m[i]

else if m[i] > max then

max := m[i] end;

// Вычисляем среднее значение и выводим результат:

IbOutput.Caption := 'Мин = '+IntToStr(min)+' Макс = '+ IntToStr(max)+' Среднее = '+FloatToStr(sum/N) ;

end;

Для создания массива используется встроенная функция Ran-dom(Max), которая возвращает случайное целое число, равномерно распределенное в диапазоне от 0 до мах-1 (мах - параметр обращения).

5.6. ПРОЦЕДУРЫ И ФУНКЦИИ

Процедуры и функции (я часто буду использовать их общее название - подпрограммы} представляют собой важный инструмент Object Pascal, позволяющий писать хорошо структурированные программы. В структурированных программах обычно легко прослеживается основной алгоритм, их проще понять любому читате лю, они удобнее в отладке и менее чувствительны к ошибкам программирования. Все эти свойства являются следствием важной особенности подпрограмм, каждая из которых представляет собой во многом самостоятельный фрагмент программы, связанный с основной программой лишь с помощью нескольких параметров. Самостоятельность подпрограмм позволяет локализовать в них все детали программной реализации того или иного алгоритмического действия, и поэтому изменение этих деталей, например, в процессе отладки обычно не приводит к изменениям основной программы.

Многие примеры в этой книге невелики по размерам (не более 30-40 строк), поэтому в таких программах можно обойтись и без подпрограмм. Иное дело - создание крупных программ в сотни, тысячи и десятки тысяч строк. Писать такие программы как нечто единое целое, без расчленения на относительно самостоятельные фрагменты, т. е. без структурирования, просто невозможно. Практически во всех языках программирования имеются средства структурирования. Языки, в которых предусмотрены такие механизмы, называются процедурно-ориентированными. К их числу принадлежит и Object Pascal.

Процедурой в Object Pascal называется особым образом оформленный фрагмент программы, имеющий собственное имя. Упоминание этого имени в тексте программы приводит к активизации процедуры и называется ее вызовом. Сразу после активизации процедуры начинают выполняться входящие в нее операторы, после выполнения последнего из них управление возвращается обратно в . основную программу и выполняются операторы, стоящие непосредственно за оператором вызова процедуры (рис. 5.8).

Для обмена информацией между основной программой и процедурой используется один или несколько параметров вызова. Как мы увидим дальше (см. гл.11), процедуры могут иметь и другой механизм обмена данными с вызывающей программой, так что параметры вызова могут и не использоваться. Если они есть, то они перечисляются в круглых скобках за именем процедуры и вместе с ним образуют оператор вызова процедуры.

Функция отличается от процедуры тем, что результат ее работы возвращается в виде значения этой функции, и, следовательно, вызов функции может использоваться наряду с другими операндами в выражениях.

Рис. 5.8. Взаимодействие вызывающей программы и процедуры

С примерами процедур и функций мы уже сталкивались - это

Стандартные процедуры Exit, ShowMessage, функции StrToInt, FioatToStr, Random, математические функции и др. Стандартными они называются потому, что созданы одновременно с системой Delphi и являются ее неотъемлемой частью. В Delphi имеется много стандартных процедур и функций. Наличие богатой библиотеки таких программных заготовок существенно облегчает разработку прикладных программ. Однако в большинстве случаев некоторые специфичные для данной прикладной программы действия не находят прямых аналогов в библиотеках Delphi, и тогда программисту приходится разрабатывать свои, нестандартные процедуры и функции.

Нестандартную подпрограмму необходимо описать, чтобы компилятор смог установить связь между оператором вызова и теми действиями, которые предусмотрены в подпрограмме. Описание подпрограммы помещается в разделе описаний (до начала исполняемых операторов).

Учебная программа UPSTRING

Не вдаваясь в дальнейшие подробности, попробуем составить собственную процедуру, чтобы пояснить сказанное. Пусть в этой процедуре преобразуется некоторая символьная строка таким образом, чтобы все строчные буквы заменялись соответствующими прописными. В Object Pascal имеется стандартная функция upcase (см. гл. 6), которая выполняет аналогичные действия над одиночным символом. Наша процедура (назовем ее upString) будет преобразовывать сразу все символы строки, причем сделаем ее пригодной не только для латинских букв, но и для букв русского алфавита[ В Delphi имеется стандартная функция AnsiUpperCase, реализующая те же действия.].

Разработку программы проведем в два этапа. Сначала сконструируем основную (вызывающую) часть программы. Ее действия очень просты: она должна получить входную строку из компонента edinput, преобразовать ее с помощью процедуры upString в выходную строку и поместить результат в iboutput.Text. Эти действия нетрудно запрограммировать в обработчике bbRunciick:

procedure TfmExample.bbRunClick(Sender: TObject);

procedure UpString(stinp: String; var stOut: String);

{ Эта процедура преобразует все буквы строки stinp в прописные и помещает результат в выходную строку stOut }

begin

stOut := stinp;

end; // UpString var

SI, S2: String;

begin

SI := edinput.Text; // Получаем исходную строку

UpString(SI,S2); // Преобразуем ее

IbOutput.Caption := S2; // Выводим результат

edinput.Text := '' ;

edinput.SetFocus ;

end;

В этой программе используется замещение процедуры UpString так называемой “заглушкой”, т. е. процедурой, в которой на самом деле не осуществляется нужных нам действий, а выходная строка просто копирует входную. (Однако эта программа синтаксически абсолютно правильна, и при желании ее можно запустить на счет.) Заглушка понадобилась нам по двум причинам. Во-первых, приведенная программа очень проста, в ней отсутствует детальная реализация процедуры, и это позволяет наглядно проиллюстрировать механизм ее описания. Во-вторых, на ее примере мы знакомимся с универсальным методом конструирования сложных программ, получившим название нисходящее программирование. В соответствии с этим методом создание программы начинается “сверху”, т. е. с разработки самого главного, генерального алгоритма. На верхнем уровне обычно еще неясны детали реализации той или иной части программы, поэтому эти части следует заменить временными заглушками. Желательно, чтобы временный вариант программы был синтаксически правильным, тогда можно его откомпилировать и убедиться в отсутствии в нем синтаксических ошибок. Такой прогон даст определенную уверенность перед разработкой и реализацией алгоритмов нижнего уровня, т. е. перед заменой заглушек реально работающими процедурами. Если реализуемый в заглушке алгоритм достаточно сложен, его вновь структурируют, выделяя главный алгоритм и применяя новые заглушки, и т. д. Процесс продолжается “вниз” до тех пор, пока не будет создан полностью работоспособный вариант программы.

Как видим, описание процедуры начинается зарезервированным словом procedure, за которым следуют имя процедуры и список формальных параметров. Список параметров заключается в круглые скобки и содержит перечень параметров с указанием их типа. Заметим, что перед параметром stout, с помощью которого в вызывающую программу возвращается результат преобразования, стоит зарезервированное слово var. Именно таким способом компилятору указываются те параметры, в которых процедура возвращает вызвавшей ее программе результат своей работы (подробнее см. гл. 10). Зарезервированное слово procedure, имя процедуры и список ее параметров образуют заголовок процедуры. За заголовком следует тело процедуры, содержащее новый раздел описаний (этот раздел пока еще пуст) и раздел исполняемых операторов (оператор stout:= stInp).

Приступим к разработке алгоритма процедуры. Для этого учтем, что в соответствии с принятой в Windows кодировкой символов (так называемой ANSI-кодировкой) символы кириллицы (русского алфавита) располагаются сплошным массивом от “А” до “я”, за исключением “ё” и “Ё”, которые предшествуют “А”. Кроме того, символ “я” имеет внутренний код 255, т. е. максимально возможное значение одного байта (для каждого символа строки выделяется по одному байту).

Вот возможный вариант процедуры:

Procedure UpString(stinp: String; var stOut: String);

{ Эта процедура преобразует все буквы строки stinp в прописные и помещает результат в выходную строку stOut } var

k: Integer;// Параметр цикла

begin

stOut := stinp;

// Цикл побуквенного преобразования

for k := 1 to Length(stOut) {Length - длина строки} do

begin

stOut[k] := UpCase(stOut[k]); // Преобразуем латиницу

if stOut[k] >= 'a' then // Строчная буква кириллицы?

st0ut[k] := // Да: преобразуем ее

Chr(ord('A') + ord(st0ut[k]) - ord('a'));

if stOut[k]='e' then

stOut[k] := 'Ё'; // Преобразуем ё в Ё

end;

end; // UpString

Комментарий к программе

В процедуре вначале с помощью оператора

stOut := stinp;

исходная строка копируется в выходную строку. Затем реализуется циклический перебор всех символов выходной строки для преобразования букв. Для доступа к отдельным символам строки используется замечательное свойство типа данных string, позволяющее рассматривать строку как набор (массив) символов. Первый символ этого набора имеет индекс 1, второй - 2 и т. д. Индекс указывается сразу за именем строки в квадратных скобках. Таким образом, stOut [i] - это г-ный символ строки stOut. Перебор символов реализован в виде счетного цикла от 1 до длины входной строки stOut (эта длина получается с помощью стандартной функции Length), в ходе которого каждый символ сначала преобразуется функцией UpCase (эта функция возвращает прописную латинскую букву для строчной буквы, передаваемой ей в параметре вызова; функция не работает с кириллицей). Затем осуществляется проверка принадлежности буквы к русской строчной (оператор if st0ut[k] >= 'a' then; символ “а” - русская буква) и осуществляется необходимая коррекция ее внутреннего кода. При коррекции используются отмеченные выше свойства непрерывности и монотонности массивов кодов, т. е. за “А” идет “Б”, за “Я” - “а”, за “а” - “б” и т. д. Стандартная функция ord возвращает внутренний код символа, который передается ей в качестве параметра обращения, а функция chr преобразует код в символ. Поскольку буква “ё” стоит особняком, ее преобразование осуществляется отдельным оператором.

Рассмотрим иной способ реализации той же программы - оформим алгоритм преобразования в виде функции:

procedure TfmExample.bbRunClick(Sender: TObject) ;

Function UpString(stinp: String): String;

{ Эта функция преобразует все буквы строки stinp в прописные и помещает результат в выходную строку Result }

var

k: Integer; // Параметр цикла begin

Result := stinp;

// Цикл побуквенного преобразования

for k := 1 to Length(Result) do

begin

Result[k] := UpCase(Result[k]);// Преобразуем латиницу

if Result[k] >= 'a' then // Строчная буква кириллицы?

Result[k] := // Да: преобразуем ее

Chr(ord('A') + ord(Result[k]) - ord('a'));

if Result[k]='e' then

Result[k] := 'Ё'; // Преобразуем ё в Ё

end;

end; // UpString

begin

{ В следующем операторе исходная строка edinput.Text преобразуется и помещается в выходную строку IbOutput.Caption: }

IbOutput.Caption := UpString(edinput.Text);

edinput.Text := '';

edinput.SetFocus ;

end;

Комментарий к программе

При описании функции необходимо за списком используемых для ее вызова параметров указать тип возвращаемого ею результата. Именно поэтому за закрывающей круглой скобкой в заголовке функции стоит двоеточие и тип string. В теле любой функции определена стандартная переменная Result, которая трактуется как результат, возвращаемый функцией.

С помощью оператора

Result := stInp;

мы сначала присвоили результату входную строку, а затем в цикле осуществили перебор символов и их коррекцию. Единственный оператор

IbOutput.Caption := UpString(edInput.Text);

заменил сразу три первых оператора в предыдущей реализации программы.